#define _GNU_SOURCE
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <time.h>
#include "sqlite3.h"
#include "zhongdunData.h"
static const char* sql_begin = "begin;";
static const char* sql_commit = "commit;";
static const char* sql_rollback = "rollback;";
static const char* dbPath = "/mnt/usr/fepRecord";
static const char* dbMobilePath = "/mnt/usr/fepRecord/mobile";
static const char* dbUnicomPath = "/mnt/usr/fepRecord/unicom";
static const char* sql_creatdb = "PRAGMA synchronous = NORMAL;PRAGMA temp_store = MEMORY; CREATE TABLE IF NOT EXISTS PHONE_INFO( \
        ID INTERGER, \
        TIMESTAMP INTEGER , \
        IMSI VARCHAR(16), \
        IMEI VARCHAR(16), \
        LAC INTEGER);";

static const char* sql_creatidx = "CREATE INDEX IF NOT EXISTS TS_INDEX ON PHONE_INFO(ID);";  
static const char* sql_selectMax = "SELECT max(ID) FROM PHONE_INFO;";
static const char* sql_selectMin = "SELECT min(ID) FROM PHONE_INFO;";
const I32 oneDay = (24 *3600);
const I32 maxDay = 90;
#define DAY_SIZE 12
typedef struct tagPHONEINFOCTR
{
    char **pResult;
    I32 Sn;
    I8 sqlCmd[512];
    I32 seqNum;
    char currentDay[DAY_SIZE] ;
    ZHONGDUNLIST dataList;
} PHONEINFOCTRL;

typedef struct tagCurrentInfo
{
    sqlite3 *currentDB ;
    char currentDay[DAY_SIZE] ;
    I32 currentSeq;
    ZHONGDUNLIST dataList;
} CurrentInfo;



PHONEINFOCTRL mobileList;
PHONEINFOCTRL unicomList;
CurrentInfo  mobileInfo;
CurrentInfo  unicomInfo;
bool initZhongDunRecordPath()
{
    DIR *pDir = NULL;
    pDir = opendir(dbPath);
    if ( pDir == NULL )
    {
        if ( mkdir(dbPath, 0777) != 0 )
        {
            cli_app_log(CLI_LOG_ERROR, "can not create dir %s \n", dbPath);
            return false;
        }

    }
    else
    {
        closedir(pDir);
    }    

    pDir = NULL;
    pDir = opendir(dbMobilePath);
    if ( pDir == NULL )
    {
        if ( mkdir(dbMobilePath, 0777) != 0 )
        {
            cli_app_log(CLI_LOG_ERROR, "can not create dir %s \n", dbMobilePath);
            return false;
        }

    }
    else
    {
        closedir(pDir);
    }

    pDir = NULL;
    pDir = opendir(dbUnicomPath);
    if ( pDir == NULL )
    {
        if ( mkdir(dbUnicomPath, 0777) != 0 )
        {
            cli_app_log(CLI_LOG_ERROR, "can not create dir %s \n", dbUnicomPath);
            return false;
        }

    }
    else
    {
        closedir(pDir);
    }
    memset(&mobileInfo, 0, sizeof(CurrentInfo));
    mobileInfo.currentDB = NULL;
    unicomInfo.currentDB = NULL;
    unicomInfo.dataList.num = 0;
    mobileInfo.dataList.num = 0;  
    memset(&unicomInfo, 0, sizeof(CurrentInfo));
    memset(&mobileList, 0, sizeof(PHONEINFOCTRL));
    memset(&unicomList, 0, sizeof(PHONEINFOCTRL));
    return true;
}

void getRecordMaxId(bool isMobile, const char *day, I32 *seqNum)
{
    char recordDay[128] = {0};
    sqlite3 *dayDB = NULL; ;
    I32 rc = 0;

    if (isMobile)
    {
        sprintf(recordDay, "%s/%s", dbMobilePath, day);
    }
    else
    {
        sprintf(recordDay, "%s/%s", dbUnicomPath, day);        
    }
    if (access(recordDay, F_OK) != 0)
    {
        cli_app_log(CLI_LOG_NOTICE, "can not find db %s \n", recordDay);
        *seqNum = 0;
        return;
    }
    *seqNum = 0;
    rc = sqlite3_open(recordDay, &dayDB);
    if (rc != SQLITE_OK)
    {
        cli_app_log(CLI_LOG_NOTICE, "can not open db %s \n", recordDay);
        return;
    }

    char **pResult;
    I32 nRow;
    I32 nCol;
    rc = sqlite3_get_table(dayDB, sql_selectMax, &pResult, &nRow, &nCol, NULL) ;   
    if ( rc !=  SQLITE_OK )
    {
        sqlite3_free_table(pResult);
        cli_app_log(CLI_LOG_ERROR, "can not get max Id %x \n", rc);
        sqlite3_close(dayDB);        
        return ;
    }
    cli_app_log(CLI_LOG_INFO, "result is %s nRows is %d \n", pResult[0], nRow);
    if ( nRow == 1 && pResult[1] != NULL )
    {    
        
        *seqNum = atoi(pResult[1]);
        cli_app_log(CLI_LOG_NOTICE, "%s max id is %d \n", recordDay, *seqNum);
        sqlite3_free_table(pResult);
        sqlite3_close(dayDB);        
        return ;
    }
    sqlite3_free_table(pResult);
    sqlite3_close(dayDB);
    return; 
}

void resetCurrentInfo(bool isMobile, CurrentInfo *currenInfo, const char *day)
{
    if (currenInfo->currentDB != NULL)
    {
        sqlite3_close(currenInfo->currentDB);  
        currenInfo->currentDB = NULL;
    }
    memset(currenInfo, 0, sizeof(CurrentInfo));
    currenInfo->currentDB = NULL;
    currenInfo->currentSeq = 0;
    I32 rc = 0;
    char buf[128] = {0};
    if (isMobile)
    {
        strcpy(mobileInfo.currentDay, day);
        sprintf(buf, "%s/%s", dbMobilePath, day);
    }
    else
    {
        strcpy(unicomInfo.currentDay, day);
        sprintf(buf, "%s/%s", dbUnicomPath, day);
    }
    rc = sqlite3_open(buf, &currenInfo->currentDB );
    if (rc != SQLITE_OK)
    {
        cli_app_log(CLI_LOG_ERROR, "can not open db %s \n", buf);
        return;
    }
    else
    {
        cli_app_log(CLI_LOG_NOTICE, "open db %s success\n", buf);
    }
    rc = sqlite3_exec(currenInfo->currentDB, sql_creatdb, 0, 0, NULL);
    if ( SQLITE_OK != rc )
    {
        cli_app_log(CLI_LOG_ERROR, "can not create table  %d \n", rc);
        sqlite3_close(currenInfo->currentDB);
        return ;
    }

    rc = sqlite3_exec(currenInfo->currentDB, sql_creatidx, 0, 0, NULL);
    if ( SQLITE_OK != rc )
    {
        cli_app_log(CLI_LOG_ERROR, "can not create idx %d \n", rc);
        sqlite3_close(currenInfo->currentDB );
        return;
    }  
    getRecordMaxId(isMobile, day, &(currenInfo->currentSeq));
 

}

void saveImsi(bool isMobile, I32 id, ZHONGDUNLIST *dataList, U32 uptime, I8 *imsi, I8 *imei, U16 lac)
{
    I32 rc = 0;
    char sqlCmd[512] = {0};
    sprintf(sqlCmd, "INSERT INTO PHONE_INFO VALUES(%d, %d, '%s', '%s', %d);", 
            id, uptime, 
            (imsi == NULL?"NULL":imsi),
            (imei == NULL?"NULL":imei),  
            lac);
    
    if (isMobile)
    {
        if (dataList->num < 100)
        {
            dataList->zhongdunList[dataList->num].uptime = uptime;
            dataList->zhongdunList[dataList->num].seqNum = id;
            dataList->zhongdunList[dataList->num].lac = lac;
            strncpy(dataList->zhongdunList[dataList->num].imsi, imsi, 16);
            strncpy(dataList->zhongdunList[dataList->num++].imei, imei, 16);
        }
        if ( mobileInfo.currentDB == NULL )
        {
            cli_app_log(CLI_LOG_ERROR, "UNexpected error in poendb\n");  
            return;
        }

        rc = sqlite3_exec(mobileInfo.currentDB, sqlCmd, 0, 0, NULL);
        if ( rc != SQLITE_OK )
        {
            cli_app_log(CLI_LOG_ERROR, "Insert phone Info failed cmd is %s failedCode %d\n" , sqlCmd, rc);
            return ;
        }
    }
    else
    {
        if (dataList->num < 100)
        {
            dataList->zhongdunList[dataList->num].uptime = uptime;
            dataList->zhongdunList[dataList->num].seqNum = id;
            dataList->zhongdunList[dataList->num].lac = lac;
            strncpy(dataList->zhongdunList[dataList->num].imsi, imsi, 16);
            strncpy(dataList->zhongdunList[dataList->num++].imei, imei, 16);
        }
        if ( unicomInfo.currentDB == NULL )
        {
            cli_app_log(CLI_LOG_ERROR, "UNexpected error in poendb\n");  
            return;
        }

        rc = sqlite3_exec(unicomInfo.currentDB, sqlCmd, 0, 0, NULL);
        if ( rc != SQLITE_OK )
        {
            cli_app_log(CLI_LOG_ERROR, "Insert phone Info failed cmd is %s failedCode %d\n" , sqlCmd, rc);
            return ;
        }

    }
    cli_app_log(CLI_LOG_DEFAULT, "save phone success id is %d imsi is %s \n", id, imsi);
    return ;
}

bool vaildDBAndLcleanDB(bool isMobile, char *fileName, I32 cleanTime)
{
    struct tm tm = {0};
    time_t thistime;
    
    if(strlen(fileName) != 8)
        return false;

    if(strptime(fileName,"%Y%m%d",&tm) == NULL)
        return false;

    thistime = mktime(&tm);
    if(thistime < 0 )
        return false;
    if (cleanTime > (I32)thistime)
    {
        char cmd[128] = {0};
        if (isMobile)
        {
            sprintf(cmd, "rm -rf %s/%s", dbMobilePath, fileName);
            system(cmd);
        }
        else
        {
            sprintf(cmd, "rm -rf %s/%s", dbUnicomPath, fileName);
            system(cmd);
        }
        cli_app_log(CLI_LOG_NOTICE, "clean database %s \n", fileName);
    }
    return true;

}

void cleanData(bool isMobile, I32 uptime)
{
    I32 cleanTime = uptime - (oneDay * maxDay);
    DIR *pDir = NULL;
    struct dirent *dt = NULL;
    if (isMobile)
        pDir = opendir(dbMobilePath);
    else
        pDir = opendir(dbUnicomPath);
    if ( pDir != NULL )
    {
        while((dt = readdir(pDir)) != NULL)
        {
            if((strcmp(dt->d_name,".") == 0) || (strcmp(dt->d_name,"..") == 0))
                continue; 
            vaildDBAndLcleanDB(isMobile, dt->d_name, cleanTime);
        }
        closedir(pDir);
    }
}

void zhongdunHandleRecord(PHONE_TMP *tmp, ZHONGDUNLIST *mobile, ZHONGDUNLIST *unicom)
{
    I32 num = 0, i = 0, rc;
    num = tmp->num;
    if ( num == 0)
    {
        return ;
    }
    PHONE_DATA *pdata;
    pdata = (PHONE_DATA *)tmp->data;  
    struct tm thistm;
    char day[DAY_SIZE] = {0};
    bool hasMobile = false;
    bool hasUnicom = false;
    for ( i = 0; i < num; i++ )
    {
        memset(day, 0, DAY_SIZE);
        localtime_r((time_t*)(&pdata->time_low), &thistm);
        sprintf(day, "%04d%02d%02d", 1900+thistm.tm_year,1+thistm.tm_mon,thistm.tm_mday);

        if (pdata->slotId == 8)
        {
            if (strcmp(day, mobileInfo.currentDay) != 0)
            {
                if (hasMobile)
                {
                    rc = sqlite3_exec(mobileInfo.currentDB, sql_commit, 0, 0, NULL);
                    if ( rc != SQLITE_OK )
                    {
                        cli_app_log(CLI_LOG_ERROR, "commit phone Info failed cmd is %s failedCode %d" , sql_commit, rc);
                        sqlite3_exec(mobileInfo.currentDB, sql_rollback, 0, 0, NULL);
                    }
                    hasMobile = false;
                }
                resetCurrentInfo(true, &mobileInfo, day);
                cleanData(true, pdata->time_low);
            }
            if (!hasMobile)
            {
                I32 rc = sqlite3_exec(mobileInfo.currentDB, sql_begin, 0, 0, NULL);
                if ( rc != SQLITE_OK )
                {
                    cli_app_log(CLI_LOG_ERROR, "start phone Info failed cmd is %s failedCode %d" , sql_begin, rc);
                }
                else
                    hasMobile = true;    
            }

           saveImsi(true,  ++mobileInfo.currentSeq, mobile, pdata->time_low, (I8 *)pdata->imsi, (I8 *)pdata->imei, pdata->lac);
        }
        else
        {
            if (strcmp(day, unicomInfo.currentDay) != 0)
            {
                if (hasUnicom)
                {
                    rc = sqlite3_exec(unicomInfo.currentDB, sql_commit, 0, 0, NULL);
                    if ( rc != SQLITE_OK )
                    {
                        cli_app_log(CLI_LOG_ERROR, "commit phone Info failed cmd is %s failedCode %d" , sql_commit, rc);
                        sqlite3_exec(unicomInfo.currentDB, sql_rollback, 0, 0, NULL);
                    }
                }
                hasUnicom = false;
                resetCurrentInfo(false, &unicomInfo, day);
                cleanData(false, pdata->time_low);
            }
            if (!hasUnicom)
            {
                I32 rc = sqlite3_exec(unicomInfo.currentDB, sql_begin, 0, 0, NULL);
                if ( rc != SQLITE_OK )
                {
                    cli_app_log(CLI_LOG_ERROR, "start phone Info failed cmd is %s failedCode %d" , sql_begin, rc);
                }
                else
                    hasUnicom = true;    
            }

           saveImsi(false,  ++unicomInfo.currentSeq, unicom, pdata->time_low, (I8 *)pdata->imsi, (I8 *)pdata->imei, pdata->lac);
        }
        pdata++;
    }
    if (hasUnicom)
    {
        rc = sqlite3_exec(unicomInfo.currentDB, sql_commit, 0, 0, NULL);
        if ( rc != SQLITE_OK )
        {
            cli_app_log(CLI_LOG_ERROR, "commit phone Info failed cmd is %s failedCode %d" , sql_commit, rc);
            sqlite3_exec(unicomInfo.currentDB, sql_rollback, 0, 0, NULL);
        }
    }
    if (hasMobile)
    {
        rc = sqlite3_exec(mobileInfo.currentDB, sql_commit, 0, 0, NULL);
        if ( rc != SQLITE_OK )
        {
            cli_app_log(CLI_LOG_ERROR, "commit phone Info failed cmd is %s failedCode %d" , sql_commit, rc);
            sqlite3_exec(mobileInfo.currentDB, sql_rollback, 0, 0, NULL);
        }
    }
}

void cleanList()
{
    unicomInfo.dataList.num = 0;
    mobileInfo.dataList.num = 0;
}

bool findOneRecord(bool isMobile, SEARCHRECORD record, ZHONGDUNDATA *data)
{
    char recordDay[128] = {0};
    sqlite3 *dayDB = NULL; ;
    I32 rc = 0;
    if (isMobile)
    {
        sprintf(recordDay, "%s/%s", dbMobilePath, record.date);
    }
    else
    {
        sprintf(recordDay, "%s/%s", dbUnicomPath, record.date);        
    }
    if (access(recordDay, F_OK) != 0)
    {
        cli_app_log(CLI_LOG_NOTICE, "can not find db %s \n", recordDay);
        return false;
    }
    rc = sqlite3_open(recordDay, &dayDB);
    if (rc != SQLITE_OK)
    {
        cli_app_log(CLI_LOG_NOTICE, "can not open db %s \n", recordDay);
        return false;
    }

    char cmd[128] = {0};
    sprintf(cmd, "SELECT * FROM PHONE_INFO WHERE ID == %d;", record.seqNum);
    char **pResult;
    I32 nRow;
    I32 nCol;
    rc = sqlite3_get_table(dayDB, cmd, &pResult, &nRow, &nCol, NULL) ;   
    if ( rc !=  SQLITE_OK )
    {
        sqlite3_free_table(pResult);
        cli_app_log(CLI_LOG_ERROR, "can not get Id %x \n", rc);
        sqlite3_close(dayDB);        
        return false;
    }
    char **dbResult = NULL;
    I8 nchar[1] = {'\0'};    
    dbResult = &pResult[nCol];
    if ( nRow >= 1 && dbResult[1] != NULL && dbResult[4] != NULL 
        && dbResult[0] != NULL && dbResult[2] != NULL)
    {    
        data->seqNum = atoi(dbResult[0]);
        data->uptime = atoi(dbResult[1]);
        strncpy(data->imsi, (dbResult[2] == NULL?nchar:dbResult[2]), 16);
        strncpy(data->imei, (dbResult[3] == NULL?nchar:dbResult[3]), 16);
        data->lac = atoi(dbResult[4]);
        cli_app_log(CLI_LOG_INFO, "search record is %s lac %d\n", data->imsi, data->lac);
        sqlite3_free_table(pResult);
        sqlite3_close(dayDB);        
        return true;
    }
    if (pResult != NULL)
        sqlite3_free_table(pResult);
    sqlite3_close(dayDB);
    return false;

}

void search50DayRecord(bool isMobile, ZHONGDUN_DB_LIST *list)
{
    time_t uptime;
    time(&uptime);
    struct tm thistm;
    I32 i = 0;
    I8 day[12] = {0};
    for (; i < 50; i++)
    {
        memset(day, 0, DAY_SIZE);
        localtime_r((time_t*)(&uptime), &thistm);
        sprintf(day, "%04d%02d%02d", 1900+thistm.tm_year,1+thistm.tm_mon,thistm.tm_mday);
        getRecordMaxId(isMobile, day, &(list->record[i].seqNum));
        uptime -= oneDay;
        strcpy(list->record[i].date, day);
    }
}

I32 refRecordSize(bool isMobile, I8 *date)
{
    date[8] = 0;
    I32 seqNum = 0;
    getRecordMaxId(isMobile, date, &seqNum);
    if (isMobile)
    {
        strcpy(mobileList.currentDay, date);
    }
    else
    {
        strcpy(unicomList.currentDay, date);
    }
    return (seqNum * 34);

}

void setSendRecordSn(bool isMobile, U16 sn)
{
    if (isMobile)
    {
        mobileList.seqNum = sn * 100;
        mobileList.Sn = sn;
    }
    else
    {
        unicomList.seqNum = sn * 100;
        unicomList.Sn = sn;
    }
    
}

bool getRecordBySn(bool isMobile, ZHONGDUNLIST *list, U16 *Sn)
{
    char recordDay[128] = {0};
    sqlite3 *dayDB = NULL; ;
    I32 rc = 0;
    I32 seqNum = 0;
    if (isMobile)
    {
        sprintf(recordDay, "%s/%s", dbMobilePath, mobileList.currentDay);
        seqNum = mobileList.seqNum;
        *Sn = mobileList.Sn;
        mobileList.seqNum += 100;
        mobileList.Sn++;
        list->num = 0;
    }
    else
    {
        sprintf(recordDay, "%s/%s", dbUnicomPath, unicomList.currentDay);    
        seqNum = unicomList.seqNum; 
        *Sn = unicomList.Sn;
        unicomList.seqNum += 100;
        unicomList.Sn++;
        list->num = 0;   
    }
    if (access(recordDay, F_OK) != 0)
    {
        cli_app_log(CLI_LOG_NOTICE, "can not find db %s \n", recordDay);
        return false;
    }
    rc = sqlite3_open(recordDay, &dayDB);
    if (rc != SQLITE_OK)
    {
        cli_app_log(CLI_LOG_NOTICE, "can not open db %s \n", recordDay);
        return false;
    }

    char cmd[128] = {0};
    sprintf(cmd, "SELECT * FROM PHONE_INFO WHERE ID > %d AND ID < %d;", seqNum, (seqNum + 101));
    char **pResult;
    I32 nRow;
    I32 nCol;
    rc = sqlite3_get_table(dayDB, cmd, &pResult, &nRow, &nCol, NULL) ;   
    if ( rc !=  SQLITE_OK )
    {
        sqlite3_free_table(pResult);
        cli_app_log(CLI_LOG_ERROR, "can not get Id %x \n", rc);
        sqlite3_close(dayDB);        
        return false;
    }
    char **dbResult = NULL;
    I8 nchar[1] = {'\0'};    
    I32 i = 0;
    I32 index = nCol;
    cli_app_log(CLI_LOG_NOTICE, "cmd is %s nRow is %d \n", cmd, nRow);
    for ( i = 0; i < nRow; i++ )
    {
        if ( list->num == 100 )
            break;
        dbResult = &pResult[index];
        index += nCol;
        if ( dbResult[1] == NULL || dbResult[4] == NULL || dbResult[0] == NULL || dbResult[2] == NULL)
        {
           cli_app_log(CLI_LOG_WARNING, "record  uptime or slotId is NULL \n");
           continue;
        }
        list->zhongdunList[list->num].uptime = atoi(dbResult[1]);
        strncpy(list->zhongdunList[list->num].imsi, (dbResult[2] == NULL?nchar:dbResult[2]), 16);
        strncpy(list->zhongdunList[list->num].imei, (dbResult[3] == NULL?nchar:dbResult[3]), 16);
        list->zhongdunList[list->num].lac = atoi(dbResult[4]);
        list->zhongdunList[list->num].seqNum = atoi(dbResult[0]);
        cli_app_log(CLI_LOG_DEFAULT, "get record  id is %d imsi is %s \n", atoi(dbResult[0]), list->zhongdunList[list->num].imsi);
        list->num++;
    }    
    if (pResult != NULL)
        sqlite3_free_table(pResult);
    sqlite3_close(dayDB);

    if (nRow >= 100)
        return true;
    else
        return false;
    

}

void cleanDataByModel(bool isMobile)
{
    if (isMobile)
    {
        if (mobileInfo.currentDB != NULL)
        {
            sqlite3_close(mobileInfo.currentDB);  
            mobileInfo.currentDB = NULL;
        }
        memset(mobileInfo.currentDay, 0, DAY_SIZE);
        system("rm /mnt/usr/fepRecord/mobile/*");

    }
    else
    {
        if (unicomInfo.currentDB != NULL)
        {
            sqlite3_close(unicomInfo.currentDB);  
            unicomInfo.currentDB = NULL;
        }
        memset(unicomInfo.currentDay, 0, DAY_SIZE);
        system("rm /mnt/usr/fepRecord/unicom/*");    
    }

}
